#include "Network.h"

#include "Error.h"
//#include "MsgQueue.h"
//#include "Oled.h"

#include "Serial.h"
#include "Tools.h"

typedef enum
{
        /* 无状态 */
        NETWORK_STATE_NONE,
        /* 正在连接到 WiFi 热点 */
        NETWORK_STATE_WIFI_CONNING,
        /* 正在查询本机 IP 地址 */
        NETWORK_STATE_QUERING_ADDR,
        /* 正在开启单链接 */
        NETWORK_STATE_OPEN_CIPMUX,
        /* 正在连接到服务器 */
        NETWORK_STATE_SERVER_CONNING,
        /* 等待 > 发送符准备就绪 */
        NETWORK_STATE_WAIT_READY_FLAG,
        /* 正在发送信息 */
        NETWORK_STATE_SENDING_MSG,
        /* 等待 OK 的回复 */
        NETWORK_STATE_WAIT_OK,
        /* 错误状态 */
        NETWORK_STATE_WAIT_ERROR,
} NetworkState;

struct _Network
{
        Serial parent_instance;
        /* ip 地址 */
        char ip_addr[16];
        /* 处理网络返回的钩子 */
        NetworkHandler handler_cb;
        /* 坑爹之处：不加 volatile 会卡死在循环 */
        volatile NetworkState state;
        volatile uint8_t      flags;
     //   MsgQueue             *mq;
};

static uint8_t is_inited = false;
static Network global_network;

//static bool    debug_fetch_mq = false;

/**
 * 处理 WiFi 连接返回的上下文，状态机处理，将连接结果保存在
 * Network 对象，处理全部完成返回 true
 */
static bool
wifi_conning_context_handle(Network *self, const uint8_t *datas)
{
        switch (datas[0]) {
        case 'O':
                self->flags |= NETWORK_FLAGS_WIFI_CONNING;
                return true;
        case 'E':
                return true;
        case '+':
                error_save((const char *)datas);
                break;
        default:
                break;
        }
        return false;
}

/**
 * 处理获取本模块 IP 地址的信息的函数，全部处理完成后返回 true
 */
static bool
wifi_get_addr_handle(Network *self, uint8_t *datas)
{
        char *p = self->ip_addr;
        if (datas[0] == 'O')
                return true;
        if (datas[0] == '+' && datas[10] == 'I') {
                datas = &datas[14];
                while (*datas != '"' && p < &self->ip_addr[15])
                        *p++ = *datas++;
                *p = '\0';
                self->flags |= NETWORK_FLAGS_QUERING_ADDR;
        }
        return false;
}

/**
 * @brief 处理 ESP-01S 返回消息的钩子函数
 */
static void
network_serial_handler_cb(Network *self, uint8_t *datas)
{
        // Debug:
        //oled_pushed_once(datas);
        //msg_queue_add_to_queue(self->mq, datas);
        /* 排除 ESP-01S 的 echo */
        if (datas[0] == 'A' && datas[1] == 'T')
                return;

        /* 判断是否为服务器端发送的信息 */
        if (datas[0] == '+' && datas[1] == 'I' && datas[2] == 'P' &&
            datas[3] == 'D') {
                while (*datas++ != ':')
                        ;
                /* 将服务器消息交给连接到网络对象信号的对象 */
                if (self->handler_cb)
                        self->handler_cb(datas);
                return;
        }

        switch (self->state) {
        case NETWORK_STATE_NONE:
                break;
        case NETWORK_STATE_WIFI_CONNING:
                if (wifi_conning_context_handle(self, datas))
                        self->state = NETWORK_STATE_NONE;
                break;
        case NETWORK_STATE_QUERING_ADDR:
                if (wifi_get_addr_handle(self, datas))
                        self->state = NETWORK_STATE_NONE;
                break;
        case NETWORK_STATE_WAIT_OK:
        case NETWORK_STATE_OPEN_CIPMUX:
                if (datas[0] == 'O')
                        self->state = NETWORK_STATE_NONE;
                break;
        case NETWORK_STATE_SERVER_CONNING:
                if (datas[0] == 'O') {
                        self->flags |= NETWORK_FLAGS_CONNECTED_TCP;
                        self->state = NETWORK_STATE_NONE;
                }
                break;
        case NETWORK_STATE_WAIT_READY_FLAG:
                if (datas[0] == '>')
                        self->state = NETWORK_STATE_NONE;
                break;
        case NETWORK_STATE_SENDING_MSG:
                if (datas[0] == 'S' && datas[5] == 'O')
                        self->state = NETWORK_STATE_NONE;
                break;
        }
}

/* 阻塞并等待某状态出现 */
static void
network_wait_state(const Network *self, NetworkState state)
{
        while (self->state != state)
                ;
        /*
        uint8_t *tmp = NULL;
        tmp          = msg_queue_get_from_queue(self->mq);
        while (tmp) {
                oled_pushed_once(tmp);
                tmp = msg_queue_get_from_queue(self->mq);
        }*/
}

/* 阻塞并等待某标志位的出现 */
static void
network_wait_flags(const Network *self, const uint8_t flags)
{
        while (!(self->flags & flags))
                ;
        /*
        uint8_t *tmp = NULL;
        tmp          = msg_queue_get_from_queue(self->mq);
        while (tmp) {
                oled_pushed_once(tmp);
                tmp = msg_queue_get_from_queue(self->mq);
        }*/
}

Network *
network_new_from_global(void)
{
        if (is_inited)
                return &global_network;
        global_network.state = NETWORK_STATE_NONE;
        global_network.flags = 0x0;
        for (int i = 0; i < 15; ++i)
                global_network.ip_addr[i] = '0';
        global_network.ip_addr[15] = '\0';
        serial_init(SERIAL(&global_network),
                    SERIAL_TYPE_USART1,
                    SERIAL_IRQ_HANDLER(network_serial_handler_cb));
        global_network.handler_cb = NULL;

        // Debug 打印消息
     //   global_network.mq = msg_queue_new_from_global();

     //   network_restore(&global_network);

        /* 关闭 ESP-01S 的 Echo */
        global_network.state = NETWORK_STATE_WAIT_OK;
        serial_send_text(SERIAL(&global_network), "ATE0\r\n");
        network_wait_state(&global_network, NETWORK_STATE_NONE);

        /* 配置 ESP-01S 为 Station 模式 */
        global_network.state = NETWORK_STATE_WAIT_OK;
        serial_send_text(SERIAL(&global_network), "AT+CWMODE=1\r\n");
        network_wait_state(&global_network, NETWORK_STATE_NONE);
        is_inited = true;
        return &global_network;
}

void
network_restore(Network *self)
{
        self->state = NETWORK_STATE_WAIT_OK;
        serial_send_text(SERIAL(self), "AT+RESTORE\r\n");
        network_wait_state(self, NETWORK_STATE_NONE);

        delay_ms(20);

        self->state = NETWORK_STATE_WAIT_OK;
        serial_send_text(SERIAL(self), "AT+RST\r\n");
        network_wait_state(self, NETWORK_STATE_NONE);

        delay_ms(20);
}

void
network_connect_wifi(Network             *self,
                     const unsigned char *ssid,
                     const unsigned char *passwd)
{
        //debug_fetch_mq = true;
        serial_send_text(SERIAL(self), "AT+CWJAP_DEF=\"");
        serial_send_text(SERIAL(self), ssid);
        serial_send_text(SERIAL(self), "\",\"");
        serial_send_text(SERIAL(self), passwd);
        serial_send_text(SERIAL(self), "\"\r\n");
        self->state = NETWORK_STATE_WIFI_CONNING;
        network_wait_flags(self, NETWORK_FLAGS_WIFI_CONNING);
        GPIO_ResetBits(GPIOB, GPIO_Pin_9);
        /* 获取本地的 IP 地址 */
        serial_send_text(SERIAL(self), "AT+CIFSR\r\n");
        self->state = NETWORK_STATE_QUERING_ADDR;
        network_wait_flags(self, NETWORK_FLAGS_QUERING_ADDR);
        GPIO_ResetBits(GPIOB, GPIO_Pin_14);
        /* 默认开启单连接模式 */
        serial_send_text(SERIAL(self), "AT+CIPMUX=0\r\n");
        self->state = NETWORK_STATE_OPEN_CIPMUX;
        network_wait_state(self, NETWORK_STATE_NONE);
        GPIO_ResetBits(GPIOB, GPIO_Pin_15);
}

void
network_connect_server(Network             *self,
                       const unsigned char *address,
                       const unsigned char *port)
{
        error_clean();
        if (!(self->flags & NETWORK_FLAGS_QUERING_ADDR)) {
                error_save("WiFi Error.");
                return;
        }

        serial_send_text(SERIAL(self), "AT+CIPSTART=\"TCP\",\"");
        serial_send_text(SERIAL(self), address);
        serial_send_text(SERIAL(self), "\",");
        serial_send_text(SERIAL(self), port);
        serial_send_text(SERIAL(self), "\r\n");
        self->state = NETWORK_STATE_SERVER_CONNING;
        network_wait_state(self, NETWORK_STATE_NONE);
}

void
network_send(Network *self, const unsigned char *datas)
{
        char atsend[22];
        if (!(self->flags & NETWORK_FLAGS_CONNECTED_TCP))
                return;

        const uint16_t len = strlen_b((const char *)datas);
        sprintf_b(atsend, "AT+CIPSEND=%d\r\n", len);

        self->state = NETWORK_STATE_WAIT_READY_FLAG;
        serial_send_text(SERIAL(self), atsend);
        network_wait_state(self, NETWORK_STATE_NONE);
        delay_ms(20);

        self->state = NETWORK_STATE_SENDING_MSG;
        serial_send_text(SERIAL(self), datas);
        network_wait_state(self, NETWORK_STATE_NONE);
        // delay_ms(100);
}

void
network_signal_connect(Network *self, const NetworkHandler handler_cb)
{
        self->handler_cb = handler_cb;
}

unsigned char
network_get_flags(Network *self)
{
        return self->flags;
}

const char *
network_get_addr(const Network *self)
{
        return self->ip_addr;
}
